<template>
	<view class="flex flex-col flex-col-top-start">
		<view class="flex flex-row flex-row-top-start relative" style="flex-wrap: wrap"
			:style="{ width: width + 'rpx' }">
			<view class="ma-5" v-for="(item, index) in _flist" :key="index" :style="{ width: itemWidth - 10 + 'rpx' }">
				<zh-sheet :round="2" :color="props.color" text :transprent="true" :padding="[0, 0]" :margin="[0, 0]"
					class=" ">
					<zh-image preview :round="2" :allowDelete="false" @delete="deletedFile(item)"
						@error="imgError(item)" extra :delete="!_disabled"
						:src="item.tempFilePath ? item.tempFilePath : item.url" :signFlag="item.tempFilePath?false:true"
						:width="itemWidth - 10" extraPosition="in" :height="itemHeight - 10" :model="props.imageModel"
						v-if="item.fileType === 'image' && (item.url || item.tempFilePath)">
						<template v-slot:extra class="relative flex-1 flex flex-col flex-col-bottom-start">
							<view v-if="!_disabled && props.showStatus" :style="{
                  background: 'rgba(0, 0, 0, 0.5)',
                  width: itemWidth - 10 + 'rpx',
                }" :class="[`round-b-${2}`]" class="py-4 px-4 flex flex-row flex-row-center-start">
								<tm-icon :font-size="23" v-if="item.statusCode == 0 || item.statusCode == 1"
									color="grey-3" name="tmicon-clock-fill"></tm-icon>
								<zh-text v-if="item.statusCode == 0 || item.statusCode == 1" color="grey-3"
									_class="pl-5" :font-size="23" :label="item.status"></zh-text>
								<tm-icon :font-size="23" v-if="item.statusCode == 2 || item.statusCode == 4" color="red"
									_class="pl-5" name="tmicon-times-circle-fill"></tm-icon>
								<zh-text v-if="item.statusCode == 2 || item.statusCode == 4" color="red" _class="pl-5"
									:font-size="23" :label="item.status"></zh-text>
								<tm-icon :font-size="23" v-if="item.statusCode == 3" color="green"
									name="tmicon-check-circle-fill"></tm-icon>
								<zh-text v-if="item.statusCode == 3" color="green" _class="pl-5" :font-size="23"
									:label="item.status"></zh-text>
							</view>

							<view v-if="props.showSort"
								class="absolute l-0 t-0 flex flex-row flex-row-center-between flex-1" :style="{
                  width: itemWidth - 10 + 'rpx',
                  height: 44 + 'rpx',
                  top: (itemHeight - 44) / 2 + 'rpx',
                }">
								<view @click.stop="prevSort(item, index, 'prev')"
									:class="[index == 0 ? 'opacity-0' : '']"
									class="flex flex-row flex-row-center-center px-12 py-4 round-tr-12 round-br-12"
									:style="{ background: 'rgba(0, 0, 0, 0.5)' }">
									<tm-icon :userInteractionEnabled="false" color="white" :font-size="22"
										name="tmicon-angle-left"></tm-icon>
								</view>
								<view @click.stop="prevSort(item, index, 'next')"
									:class="[index == _flist.length - 1 ? 'opacity-0' : '']"
									class="flex flex-row flex-row-center-center px-12 py-4 round-tl-12 round-bl-12"
									:style="{ background: 'rgba(0, 0, 0, 0.5)' }">
									<tm-icon :userInteractionEnabled="false" color="white" :font-size="22"
										name="tmicon-angle-right"></tm-icon>
								</view>
							</view>
						</template>
					</zh-image>
					<zh-video :round="2" :allowDelete="false" @delete="deletedFile(item)" extra delete
						:src="item.tempFilePath ? item.tempFilePath : item.url" :signFlag="item.tempFilePath?false:true"
						:width="itemWidth - 10" extraPosition="in" :height="itemHeight - 10"
						v-if="item.fileType === 'video' && (item.url || item.tempFilePath)">
						<template v-slot:extra class="relative flex-1 flex flex-col flex-col-bottom-start">
							<view :style="{
                  background: 'rgba(0, 0, 0, 0.5)',
                  width: itemWidth - 20 + 'rpx',
                }" :class="[`round-b-${2}`]" class="py-4 px-4 flex flex-row flex-row-center-start">
								<tm-icon :font-size="23" v-if="item.statusCode == 0 || item.statusCode == 1"
									color="grey-3" name="tmicon-clock-fill"></tm-icon>
								<zh-text v-if="item.statusCode == 0 || item.statusCode == 1" color="grey-3"
									_class="pl-5" :font-size="23" :label="item.status"></zh-text>
								<tm-icon :font-size="23" v-if="item.statusCode == 2 || item.statusCode == 4" color="red"
									_class="pl-5" name="tmicon-times-circle-fill"></tm-icon>
								<zh-text v-if="item.statusCode == 2 || item.statusCode == 4" color="red" _class="pl-5"
									:font-size="23" :label="item.status"></zh-text>
								<tm-icon :font-size="23" v-if="item.statusCode == 3" color="green"
									name="tmicon-check-circle-fill"></tm-icon>
								<zh-text v-if="item.statusCode == 3" color="green" _class="pl-5" :font-size="23"
									:label="item.status"></zh-text>
							</view>

							<view v-if="props.showSort"
								class="absolute l-0 t-0 flex flex-row flex-row-center-between flex-1" :style="{
                  width: itemWidth - 10 + 'rpx',
                  height: 44 + 'rpx',
                  top: (itemHeight - 44) / 2 + 'rpx',
                }">
								<view @click.stop="prevSort(item, index, 'prev')"
									:class="[index == 0 ? 'opacity-0' : '']"
									class="flex flex-row flex-row-center-center px-12 py-4 round-tr-12 round-br-12"
									:style="{ background: 'rgba(0, 0, 0, 0.5)' }">
									<tm-icon :userInteractionEnabled="false" color="white" :font-size="22"
										name="tmicon-angle-left"></tm-icon>
								</view>
								<view @click.stop="prevSort(item, index, 'next')"
									:class="[index == _flist.length - 1 ? 'opacity-0' : '']"
									class="flex flex-row flex-row-center-center px-12 py-4 round-tl-12 round-bl-12"
									:style="{ background: 'rgba(0, 0, 0, 0.5)' }">
									<tm-icon :userInteractionEnabled="false" color="white" :font-size="22"
										name="tmicon-angle-right"></tm-icon>
								</view>
							</view>
						</template>
					</zh-video>
					<zh-sheet :eventPenetrationEnabled="true" :followTheme="props.followTheme" :round="2"
						v-if="!item.url && !item.tempFilePath" :color="props.disabled ? 'grey' : props.color" text
						:padding="[0, 0]" :margin="[0, 0]" _class="flex-center" :height="itemHeight - 10"
						@click="chooseFile(item)">
						<slot name="icon">
							<tm-icon :font-size="42" :userInteractionEnabled="false"
								:name="item.fileType === 'video' ? 'tmicon-ios-videocam' : 'tmicon-plus'"></tm-icon>
						</slot>
						<zh-text :label="item.remarks" _class="text-align-center"></zh-text>
					</zh-sheet>
				</zh-sheet>
			</view>
			<view @click="chooseFile" v-if="!_disabledAdd" class="ma-5" :style="{ width: itemWidth - 10 + 'rpx' }">
				<zh-sheet :eventPenetrationEnabled="true" :followTheme="props.followTheme" :round="2"
					:color="props.color" text :padding="[0, 0]" :margin="[0, 0]" _class="flex-center"
					:height="itemHeight - 10">
					<slot name="icon">
						<tm-icon :font-size="42" :userInteractionEnabled="false"
							:name="hasTypeCount(['video']) === 1 ? 'tmicon-ios-videocam' : 'tmicon-plus'"></tm-icon>
					</slot>
				</zh-sheet>
			</view>
		</view>
	</view>
</template>
<script lang="ts" setup>
	/**
 * 上传
 * @description 图片上传组件。
 */
	import { computed, ref, PropType, Ref, watch, toRaw, nextTick, getCurrentInstance, inject, reactive, onMounted, isRef, isProxy } from 'vue';
	import { file, fileConfig, statusCode, uploadfile } from './upload';
	import { isUrl } from '@/common/utils/util';
	import commonApi from '@/api/modules/common';
	import { getFileSignParams } from '@/common/utils/signatureUtil';
	const proxy = getCurrentInstance()?.proxy ?? null;
	const props = defineProps({
		//是否跟随全局主题的变换而变换
		followTheme: {
			type: [Boolean, String],
			default: true,
		},
		width: {
			type: Number,
			default: 638,
		},
		//一行排列几个。
		rows: {
			type: Number,
			default: 5,
		},
		//图片的高度
		imageHeight: {
			type: Number,
			default: 140,
		},
		imageModel: {
			type: String,
			default: 'scaleToFill',
		},
		defaultValue: {
			type: [String, Object, Array as PropType<Array<file>>],
			default: () => [],
		},
		//可以是双向绑定
		modelValue: {
			type: [String, Array],
			default: () => [],
		},
		fileList: {
			type: [String, Object, Array as PropType<Array<file>>],
			default: () => [],
		},
		//返回类型：url showId
		returnType: {
			type: String,
			default: 'showId',
		},
		//返回格式 string 字符串 多个用英文逗号隔开，array 数组
		modelType: {
			type: String,
			default: 'string',
		},
		color: {
			type: String,
			default: 'primary',
		},
		header: {
			type: Object,
			default: () => { },
		},
		formData: {
			type: Object,
			default: () => { },
		},
		//最多可选择文件
		maxFile: {
			type: Number,
			default: 9,
		},
		//每次可选择的文件数量
		count: {
			type: Number,
			default: 1,
		},
		//图片上传最大文件大小 默认10M
		maxSize: {
			type: Number,
			default: 10 * 1024 * 1024,
		},
		//视频上传最大文件大小 默认500M
		maxVideoSize: {
			type: Number,
			default: 500 * 1024 * 1024,
		},
		url: {
			type: String,
			default: () => {
				return commonApi.fileUploadUrl.url;
			},
		},
		//是否压缩
		compressed: {
			type: Boolean,
			default: true,
		},
		////视频长度
		maxDuration: {
			type: Number,
			default: 60,
		},
		//文件类型 image/video/file
		fileType: {
			type: Array,
			default: () => ['image'],
		},
		//original 原图，compressed 压缩图，默认二者都有
		sizeType: {
			type: Array,
			default: () => ['original', 'compressed'],
		},
		////album 从相册选图，camera 使用相机，默认二者都有
		sourceType: {
			type: Array,
			default: () => ['album', 'camera'],
		},
		formName: {
			type: String,
			default: 'file',
		},
		autoUpload: {
			type: Boolean,
			default: true,
		},
		disabled: {
			type: Boolean,
			default: false,
		},
		//删除前执行，如果返回false,将阻止删除。
		onRemove: {
			type: [Function, Boolean],
			default: () => {
				return (item : file) => true;
			},
		},
		//开始上传前执行，如果返false，将阻止上传，
		onStart: {
			type: [Function, Boolean],
			default: () => {
				return (item : file) => true;
			},
		},
		//上传成功后，从服务器响应后立即执行，此时，还未更改当前文件上传的状态，是成功还是失败
		//如果此时返回false,将会让文件状态从成功改为上传失败，尽管 从服务器正确返回，但仍然显示失败。
		onSuccessAfter: {
			type: [Function, Boolean],
			default: () => {
				return (item : file) => true;
			},
		},
		//选择文件前执行，如果此时返回false,将阻止选择文件。你可以在这里做一些上传前的配置。
		beforeChooese: {
			type: [Function, Boolean],
			default: () => {
				return (item : file) => true;
			},
		},
		chooesefileAfter: {
			type: [Function, Boolean],
			default: () => {
				return (item : file[]) => item;
			},
		},
		//是否显示排序功能
		showSort: {
			type: Boolean,
			default: false,
		},
		//文件分类
		fileClassCode: {
			type: String,
			default: '99',
		},
		/**服务器返回的状态码，默认200成功 */
		statusCode: {
			type: Number,
			default: 200,
		},
		showStatus: {
			type: Boolean,
			default: true,
		},
	});
	/**
	 * emits 事件说明
	 * @method succcess 一个文件上传成功后立即执行， 返回：file,fileList
	 * @method fail 一个文件上传失败后立即执行， 返回：file,fileList
	 * @method complete 所有文件上传完成， 返回：file,fileList
	 * @method change 文件列表增加或者减少,文件状态的改变， 返回：fileList
	 * @method remove 一个文件被移除时触发。file,fileList
	 * @method uploadComplete i当前任务所有文件上传结束时触发。fileList
	 */
	const emits = defineEmits(['success', 'fail', 'complete', 'change', 'remove', 'uploadComplete', 'update:modelValue', 'update:fileList']);
	let timeId : any = NaN;
	const itemWidth = computed(() => {
		return props.width / props.rows;
	});
	const itemHeight = computed(() => {
		return props.imageHeight;
	});

	const _uploadObj = new uploadfile({
		formName: props.formName,
		hostUrl: props.url,
		autoUpload: props.autoUpload,
		fileList: addSuccess(props.fileList && props.fileList.length > 0 ? props.fileList : props.modelValue),
		defaultList: addDefaultList(props.defaultValue),
		header: props.header,
		formData: props.formData,
		maxFile: props.maxFile,
		count: props.count,
		maxSize: props.maxSize,
		maxVideoSize: props.maxVideoSize,
		compressed: props.compressed,
		sizeType: props.sizeType,
		sourceType: props.sourceType,
		fileType: props.fileType,
		maxDuration: props.maxDuration,
		fileClassCode: props.fileClassCode,
		statusCode: props.statusCode,
	});

	const _flist : Ref<Array<any>> = ref([]);
	const _disabled = computed(() => props.disabled);
	const _disabledAdd = computed(() => {
		if (props.disabled) {
			return props.disabled;
		}

		if (_flist.value.length >= props.maxFile) {
			return true;
		} else {
			if (_flist.value.length > 0) {
				let flag = false;
				_flist.value.forEach((item) => {
					if (!item.url) {
						flag = true;
					}
				});
				return flag;
			}
		}
	});

	_flist.value = [..._uploadObj.filelist];

	const _blackValue = uni.$tm.u.deepClone(toRaw(_uploadObj.filelist));
	function hasTypeCount(keys : string[]) : number {
		//包含类型数量
		let count = 0;
		keys.forEach((key) => {
			if (props.fileType.findIndex((item : string) => item === key) > -1) count++;
		});
		return count;
	}
	watch(
		[() => props.header, () => props.maxFile, () => props.maxSize, () => props.formData],
		() => {
			_uploadObj.setConfig({
				formName: props.formName,
				hostUrl: props.url,
				header: props.header,
				formData: props.formData,
				maxFile: props.maxFile,
				count: props.count,
				maxSize: props.maxSize,
				maxVideoSize: props.maxVideoSize,
				compressed: props.compressed,
				sizeType: props.sizeType,
				sourceType: props.sourceType,
				fileType: props.fileType,
				maxDuration: props.maxDuration,
				fileClassCode: props.fileClassCode,
			});
		},
		{ deep: true }
	);

	function prevSort(item : file, index : number, type : 'prev' | 'next') {
		if ((index == 0 && type == 'prev') || (index == _flist.value.length - 1 && type == 'next')) {
			return;
		}
		let nowindex = type == 'prev' ? index - 1 : index + 1;
		let nowItem : file = toRaw(_flist.value[index]);
		let newnowItem : file = toRaw(_flist.value[nowindex]);
		let nowfilelist : Array<file> = uni.$tm.u.deepClone(toRaw(_uploadObj.filelist));
		nowfilelist.splice(index, 1, newnowItem);
		nowfilelist.splice(nowindex, 1, nowItem);
		_uploadObj.filelist = [...nowfilelist];
		_flist.value = [..._uploadObj.filelist];
		updateValue();
	}
	function imgError(item : file) {
		let index = _flist.value.findIndex((el) => el.uid == item.uid);
		if (index > -1) {
			let p = [..._flist.value];
			p.splice(index, 1, {
				...item,
				status: '加载失败',
				statusCode: statusCode.fail,
			});
			_flist.value = [...p];
		}
	}
	function updateValue() {
		emits('update:fileList', _flist.value);
		let list : Array<string> = [];
		if (_flist.value && _flist.value.length > 0) {
			let flag = false;
			_flist.value.forEach((item) => {
				let showId = item.showId;
				let url = item.url;
				if (item.statusCode == 3) {
					if (props.returnType === 'showId') {
						list.push(showId);
					} else {
						list.push(url);
					}
					flag = true;
				} else {
					if (item.defaultShow) {
						list.push('');
					}
				}
			});
			if (!flag) {
				list = [];
			}
		}

		if (props.modelType === 'string' || props.modelType === 'String') {
			let retValue = '';
			if (list.length > 0) {
				list.forEach((item) => {
					retValue += item + ',';
				});
				const reg = /,$/gi;
				retValue = retValue.replace(reg, '');
			}
			emits('update:modelValue', retValue);
		} else {
			emits('update:modelValue', list);
		}
	}
	function addDefaultList(file : any = []) {
		let fileList = [] as any;
		if (Array.isArray(file)) {
			fileList = file;
		} else {
			fileList = file.split(',');
		}
		let fl = fileList.map((e : any) => {
			let _itemfile : any = { remarks: '' };
			if (typeof e == 'string') {
				_itemfile.remarks = e;
			} else {
				_itemfile = { ...e };
			}
			_itemfile = {
				..._itemfile,
				defaultShow: true,
			};
			return _itemfile;
		});
		return fl;
	}
	//添加已上传文件列表。
	function addSuccess(file : any = []) {
		let fileList = [];
		if (Array.isArray(file)) {
			fileList = file;
		} else {
			if (file) {
				fileList = file.split(',');
			}
		}
		let fl = fileList.map((e : any) => {
			let _itemfile : any = { url: '' };
			if (e) {
				if (typeof e == 'string') {
					if (isUrl(e, false)) {
						_itemfile.url = e;
					} else {
						_itemfile.url = commonApi.fileShowUrl.url + e;
						_itemfile.showId = e;
					}
				} else {
					_itemfile = { ...e };
					if (!_itemfile.url) {
						if (_itemfile.showId) {
							_itemfile.url = commonApi.fileShowUrl.url + _itemfile.showId;
						}
					}
				}
			}

			let code = e?.statusCode ?? statusCode.success;
			if (!_itemfile.url) {
				code = '';
			}
			_itemfile = {
				statusCode: code,
				status: e?.status ?? '上传成功',
				progress: e?.progress ?? 100,
				..._itemfile,
			};
			return _itemfile;
		});
		return fl;
	}

	//选择图片前执行。
	_uploadObj.beforeChooesefile = async function () {
		if (typeof props.beforeChooese === 'function') {
			let p = await props.beforeChooese();
			if (typeof p === 'function') {
				p = await p();
			}
			if (!p) return false;
		}
		return true;
	};
	_uploadObj.chooesefileAfter = async function (filelist : file[]) {
		let tep = uni.$tm.u.deepClone(filelist);
		if (typeof props.chooesefileAfter === 'function') {
			let p = await props.chooesefileAfter(tep);
			if (typeof p === 'function') {
				p = await p(tep);
			}
			return p;
		}
		return tep;
	};
	//上传成功后，即将更新文件状态前执行，如果返回false，文件将标记为失败，尽管已经上传成功。
	_uploadObj.beforeSuccess = async function (item : file) {
		if (typeof props.onSuccessAfter === 'function') {
			let p = await props.onSuccessAfter(item);
			if (typeof p === 'function') {
				p = await p(item);
			}
			if (!p) return false;
		}

		return true;
	};
	//开始上传前执行。
	_uploadObj.beforeStart = async function (item : file) {
		if (typeof props.onStart === 'function') {
			let p = await props.onStart(item);
			if (typeof p === 'function') {
				p = await p(item);
			}
			if (!p) return false;
		}
		return true;
	};
	//任何一个文件上传结束时都会触发。
	_uploadObj.complete = function (item : file) {
		// _filelist.value = [..._uploadObj.filelist]
		// emits("complete",toRaw(item),toRaw(_filelist.value));
		// emits("update:modelValue",_filelist.value)
		_flist.value = [..._uploadObj.filelist];
		updateValue();
	};
	//自动监听加入已上传文件到列表中。

	watch(
		() => props.modelValue,
		() => {
			clearTimeout(timeId);

			timeId = setTimeout(function () {
				let fileList = [..._uploadObj.filelist];
				_uploadObj.clear();
				let newList = addSuccess(props.modelValue);
				let i = 0;
				let fl = newList.map((newItem : any) => {
					let _itemfile = { ...newItem };
					let j = 0;
					for (const item of fileList) {
						if (j >= i) {
							if (newItem.showId && newItem.showId === item.showId) {
								_itemfile = { ...item };
								i++;
								break;
							} else {
								if (newItem.url && newItem.url === item.url) {
									_itemfile = { ...item };
									i++;
									break;
								}
							}
						}

						j++;
					}
					return _itemfile;
				});
				_uploadObj.addFile(fl);
				_flist.value = [...uni.$tm.u.deepClone(_uploadObj.filelist)];
				emits('update:fileList', _flist.value);
			}, 200);
		},
		{ deep: true }
	);
	_uploadObj.uploadComplete = function (filelist) {
		emits('uploadComplete', filelist);
	};
	_uploadObj.success = function (item, fileList) {
		let files = uni.$tm.u.deepClone(_uploadObj.filelist);
		emits('success', toRaw(item), files);
		emits('change', files);
	};
	_uploadObj.fail = function (item) {
		let files = uni.$tm.u.deepClone(_uploadObj.filelist);
		emits('fail', toRaw(item), files);
		emits('change', files);
	};
	function chooseFile(item : any) {
		if (_disabled.value) return;
		_uploadObj.chooesefile(item).then((fileList) => {
			_flist.value = [..._uploadObj.filelist];
		});
	}
	_uploadObj.progress = function (item, index) {
		_flist.value[index].status = item.progress + '% ' + item.status;
		_flist.value[index].statusCode = item.statusCode
	}
	async function deletedFile(item : file) {
		if (item.statusCode == 1) {
			uni.showToast({
				title: '上传中不允许删除',
				icon: 'none',
				mask: true
			})
			return;
		}
		if (typeof props.onRemove === 'function') {
			let p = await props.onRemove(item);
			if (typeof p === 'function') {
				p = await p(item);
			}
			if (!p) return false;
		}
		_uploadObj.delete(item);
		_flist.value = [..._uploadObj.filelist];
		emits('remove', toRaw(item));
		updateValue();
		emits('change', toRaw(_uploadObj.filelist));
	}

	function clear() {
		_uploadObj.clear();
		_uploadObj.filelist = [];
		_flist.value = [];
		updateValue();
	}
	//fileId标识id.
	function del(fileId : number | string) {
		let index = _uploadObj.filelist.findIndex((el) => el.uid == fileId)
		if (index > -1) {
			const item = _uploadObj.filelist[index]
			if (item.statusCode == 1) {
				uni.showToast({
					title: '上传中不允许删除',
					icon: 'none',
					mask: true
				})
				return;
			}
			_uploadObj.delete(item)
			_flist.value = _flist.value.splice(index, 1)
			emits('remove', toRaw(item))
			emits('update:modelValue', _uploadObj.filelist)
			emits('change', toRaw(_uploadObj.filelist))
		}

	}
	function getFailList() {
		return _uploadObj.filelist.filter((el) => el.statusCode != statusCode.fail && el.statusCode != statusCode.max);
	}
	function getSuccessList() {
		return _uploadObj.filelist.filter((el) => el.statusCode != statusCode.success);
	}
	function clearFail() {
		const list = _uploadObj.filelist.filter((el) => el.statusCode != statusCode.fail && el.statusCode != statusCode.max);
		_flist.value = [...list];
		updateValue();
	}
	/**
	 * ref 手动调用的方法
	 * start 开始上传
	 * stop 停止上传
	 * clear 清除所有文件
	 * del 删除某一项文件。
	 * getFailList 获取上传失败的文件列表。
	 * getSuccessList 获取上传成功的文件列表
	 * clearFail 清除上传失败的文件（只要标识不是成功的都会删除）
	 * 手动
	 */
	defineExpose({
		start: () => {
			return _uploadObj.start();
		},
		stop: () => {
			return _uploadObj.stop();
		},
		clear,
		del,
		getFailList,
		clearFail,
	});
</script>